package com.basketball.gym.utils;


import com.basketball.gym.exception.RedisTokenErrorException;
import com.basketball.gym.exception.RedisTokenNullException;
import com.basketball.gym.pojo.Admin;
import com.basketball.gym.pojo.Role;
import com.basketball.gym.pojo.state.RedisHeader;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Service
@Setter
@ConfigurationProperties(prefix = "jwt")
public class JwtService {
    @Autowired
    private RedisTemplate<String, Role> roleRedisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
    /**
     * token存活时间
     */
    private Long ACCESS_TOKEN_EXPIRATION = 24 * 3600L * 1000;
    /**
     * refreshToken存活时间
     */
    private Long REFRESH_TOKEN_EXPIRATION = 15 * 24 * 3600L * 1000;
    /**
     * jwt所有者
     */
    private String SUBJECT;
    /**
     * jwt签发者
     */
    private String JWT_ISS;
    /**
     * jwt密钥
     */
    private String secret;


    /**
     * 生成accessToken
     *
     * @param userId：用户ID
     * @return accessToken
     */
    public String getAccessToken(int userId, String role) {
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("role", role);
        return Jwts.builder().setClaims(claims)
                .setHeader(header)
                .setIssuer(JWT_ISS)
                .setSubject(SUBJECT)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION))
                .signWith(SIGNATURE_ALGORITHM, secret)
                .compact();
    }

    /**
     * 生成refreshToken
     */
    public String getRefreshToken(int userId, String role) {
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("role", role);
        return Jwts.builder().setClaims(claims)
                .setHeader(header)
                .setIssuer(JWT_ISS)
                .setSubject(SUBJECT)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION))
                .signWith(SIGNATURE_ALGORITHM, secret)
                .compact();
    }

    public UserDetails getUserDetails(String token) throws RedisTokenNullException, RedisTokenErrorException {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        int userId = (int) claims.get("userId");
        String role = (String) claims.get("role");
        if (!role.equals("user")) {
            String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.ADMIN_TOKEN.getHeader() + userId);
            if (rightToken == null) {
                throw new RedisTokenNullException();
            }
            if (!rightToken.equals(token)) {
                throw new RedisTokenErrorException();
            }
            return User.withUsername(new String(String.valueOf(userId))).password("123456").roles(role).build();
        } else {
            String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.TOKEN.getHeader() + userId);
            if (rightToken == null) {
                throw new RedisTokenNullException();
            }
            if (!rightToken.equals(token)) {
                throw new RedisTokenErrorException();
            }
            return User.withUsername(new String(String.valueOf(userId))).password("123456").roles(role).build();
        }

    }

    /**
     * 解密Token，得到UserDetails
     *
     * @param token：token
     * @param request：httpServletRequest,存储用户信息
     * @return UserDetails
     */
    public UserDetails getUserDetails(String token, HttpServletRequest request) throws RedisTokenNullException, RedisTokenErrorException {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        int userId = (int) claims.get("userId");
        String role = (String) claims.get("role");
        if (!role.equals("user")) {
            String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.ADMIN_TOKEN.getHeader() + userId);
            System.out.println("token" + rightToken);
            if (rightToken == null) {
                throw new RedisTokenNullException();
            }
            if (!rightToken.equals(token)) {
                throw new RedisTokenErrorException();
            }
            Admin admin = new Admin(userId, role);
            request.setAttribute("activeAdmin", admin);
            return User.withUsername(new String(String.valueOf(userId))).password("123456").roles(role).build();
        } else {
        }
        String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.TOKEN.getHeader() + userId);
        if (rightToken == null) {
            throw new RedisTokenNullException();
        }
        if (!rightToken.equals(token)) {
            throw new RedisTokenErrorException();
        }
        com.basketball.gym.pojo.User user = new com.basketball.gym.pojo.User();
        user.setId(userId);
        request.setAttribute("activeUser", user);
        return User.withUsername(new String(String.valueOf(userId))).password("123456").roles(role).build();


    }

    /**
     * 获得用户信息
     *
     * @param token：token
     * @return user
     */
    public Object getUser(String token) {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        int userId = (int) claims.get("userId");
        String role = (String) claims.get("role");
        if (!role.equals("user")) {
            return new Admin(userId, role);
        } else {
            com.basketball.gym.pojo.User user = new com.basketball.gym.pojo.User();
            user.setId(userId);
            return user;
        }
    }

    /**
     * socket获取用户
     */
    public com.basketball.gym.pojo.User getUserNotAdmin(String token) {
        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            int userId = (int) claims.get("userId");
//            System.out.println(userId+"userId");
            String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.TOKEN.getHeader() + userId);
            if (rightToken == null) {
                return null;
            }
            if (!rightToken.equals(token)) {
                return null;
            }
            List<Role> roles = roleRedisTemplate.opsForList().range(RedisHeader.ROLE.getHeader() + userId, 0, -1);
            return new com.basketball.gym.pojo.User(userId, roles);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取UserId
     */
    public int getUserId(String token) throws RedisTokenNullException, RedisTokenErrorException {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        int userId = (int) claims.get("userId");
        Boolean isAdmin = (Boolean) claims.get("isAdmin");
        if (isAdmin) {
            String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.ADMIN_TOKEN.getHeader() + userId);
            if (rightToken == null) {
                throw new RedisTokenNullException();
            }
            if (!rightToken.equals(token)) {
                throw new RedisTokenErrorException();
            }
        } else {
            String rightToken = stringRedisTemplate.opsForValue().get(RedisHeader.TOKEN.getHeader() + userId);
            if (rightToken == null) {
                throw new RedisTokenNullException();
            }
            if (!rightToken.equals(token)) {
                throw new RedisTokenErrorException();
            }
        }
        return userId;
    }

}
